﻿#include "MyCustomPlot.h"

#pragma execution_character_set("utf-8")

MyCustomPlot::MyCustomPlot(QWidget *parent, double freq, XAxisType xType, double xLimit)
    : m_Plot(nullptr),
      m_rangeTime(5),
      m_freq(freq),
      m_currentkey(0),
      m_sensorsCount(0),
      m_xType(xType),
      m_timePoint(0),
      m_xLimit(xLimit)
{
    m_rangeSize = xLimit / freq;
    m_Plot = dynamic_cast<QCustomPlot *>(parent);
    // m_customPlot->yAxis->setTickLabels(true);
    //  设置滚动鼠标修改功能
    m_Plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
    m_Plot->setOpenGl(false);
    m_Plot->setNoAntialiasingOnDrag(true);

    qDebug() << "opengl open" << m_Plot->openGl();
}

MyCustomPlot::~MyCustomPlot()
{
    if (m_Plot) {
        delete m_Plot;
    }
}

/// @brief 初始化绘图界面
/// @param signalCounts 信号个数
/// @param bg 背景颜色
/// @param pens 画笔颜色
/// @param xType x轴坐标类型（计数点、时间）
void MyCustomPlot::initPlot(const int &signalCounts, const QColor &bg, const QColor &labelColor,
                            const QVector<QColor> &pens, const QString &labelName)
{
    // 设置背景色
    m_Plot->setBackground(bg);
    m_sensorsCount = signalCounts;

    // 设置X、Y轴属性
    m_Plot->yAxis->setTickLabels(true);
    m_Plot->yAxis->setVisible(true); // 显示左侧Y轴
    m_Plot->yAxis->setLabel(labelName);
    m_Plot->yAxis->setLabelColor(labelColor);
    m_Plot->yAxis->setRange(-1, 1); // 设置左侧Y轴范围的下限和上限

    m_Plot->yAxis2->setVisible(true); // 显示右侧Y轴
    m_Plot->yAxis2->setRange(-1, 1);  // 设置右侧Y轴范围的下限和上限

    QPen tickPen(labelColor);
    m_Plot->xAxis->setTickPen(tickPen);
    m_Plot->xAxis->setTickLabelColor(labelColor);
    m_Plot->yAxis->setTickPen(tickPen);
    m_Plot->yAxis->setTickLabelColor(labelColor);

    m_graphPens = pens;
    // 创建QCPGraph对象
    addGraphs();
}

/// @brief 添加绘制数据
/// @param keysArray 横坐标数组
/// @param valuesArray 信号值数组
void MyCustomPlot::addDatas(const QVector<QVector<double>> &keysArray, const QVector<QVector<double>> &valuesArray)
{
    std::size_t dataSize = keysArray[0].size(); // 一定有传感器，所以keysArray不会为空，取第一个传感器，看有多少数据
    for (int i = 0; i < dataSize; i++) {
        /*判断数据是否超过限定范围，超过则清除历史数据*/
        if (m_xType == MyCustomPlot::KdateTime) {
            double difTime = (QDateTime::currentMSecsSinceEpoch() - m_timePoint) * 0.001;
            // 超过指定时间，就清空之前数据
            if (difTime > m_rangeTime) {
                for (int j = 0; j < m_sensorsCount; j++) {
                    m_Graph[j]->data()->removeBefore(keysArray[j][i] * 0.001 - m_rangeTime);
                }
                setStartTime();
            }
        } else {
            // 当横坐标每次达到设定x轴范围的整数倍时，就将x轴最左边数据清理（如果不清理，绘图性能会大幅下降）
            if (static_cast<int>(m_currentkey) % static_cast<int>(m_rangeSize) == 0 && m_currentkey >= m_rangeSize) {
                // if ((int)m_currentkey % (int)m_rangeSize == 0 && m_currentkey > 0) {
                for (int j = 0; j < m_sensorsCount; j++) {
                    m_Graph[j]->data()->removeBefore(m_currentkey - m_rangeSize);
                }
            }
        }

        /*添加新数据*/
        for (int j = 0; j < m_sensorsCount; j++) {
            QPointer<QCPGraph> graph = m_Graph[j];
            // double test = qSin(graph->dataCount() / 50.0) + qSin(graph->dataCount() / 50.0 / (0.3843 + j)) * 0.25;
            if (m_xType == MyCustomPlot::KdateTime) {
                graph->addData(keysArray[j][i] * 0.001, valuesArray[j][i]);
            } else {
                graph->addData(m_currentkey, valuesArray[j][i]);
            }

            // 使键轴范围与数据滚动
            graph->keyAxis()->rescale();
            graph->rescaleValueAxis(false, true);
        }
        // graph->keyAxis()->setRange(key * 0.001, 20, Qt::AlignRight); // 坐标范围为20秒以内的数据

        /*设置x轴范围*/
        // 将X轴的范围设置为当前范围的上限值不变,为实现向右移动X轴范围的效果。
        // Qt::AlignRight 参数用于指定对齐方式，确保X轴的范围在右侧对齐，主要实现向右移动的效果。
        if (m_xType == MyCustomPlot::KdateTime) {
            m_Plot->xAxis->setRange(keysArray[0][i] * 0.001, m_rangeTime, Qt::AlignRight);
        } else {
            m_Plot->xAxis->setRange(m_currentkey, m_rangeSize, Qt::AlignRight);
            m_currentkey += (static_cast<double>(1) / m_freq);
        }

        /*设置画笔的显示*/
        for (int j = 0; j < m_sensorsCount; j++) {
            QPointer<QCPGraph> graph = m_Graph[j];
            double graphValue = graph->dataMainValue(graph->dataCount() - 1);

            QPointer<AxisTag> tag = m_Tag[j];
            tag->updatePosition(graphValue);
            tag->setText(QString::number(graphValue, 'f', 2));
        }
        // m_Plot->replot(QCustomPlot::rpQueuedReplot);
    }
    m_Plot->replot(QCustomPlot::rpQueuedReplot);
}

/// @brief 根据采样频率重新设置X轴范围
/// @param freq 频率
void MyCustomPlot::setXAxisRange(double freq)
{
    int tempFreq = freq;
    // 当频率超过1000时，会根据频率进行数据抽样，因此X轴范围保持频率为1000的大小
    if (freq > 1000) {
        tempFreq = 1000;
    }
    m_rangeSize = m_xLimit / tempFreq;
    m_freq = tempFreq;

    // 此时需要保持X轴原有类型
    if (m_xType == MyCustomPlot::KNumber) {
        setXAxisToNumber();
    } else if (m_xType == MyCustomPlot::KdateTime) {
        setXAxisToTime(freq);
    }
}

/// @brief 设置开始绘制时间（清空数据之后更新该值）
void MyCustomPlot::setStartTime()
{
    m_timePoint = QDateTime::currentMSecsSinceEpoch();
}

/// @brief 设置传感器数量
/// @param sensorsCount 传感器数量
void MyCustomPlot::setSensorsCount(int sensorsCount)
{
    // 先清理所有数据
    for (int j = 0; j < m_sensorsCount; j++) {
        m_Graph[j]->data()->clear();
    }
    // 清理现有的图形
    m_Plot->clearGraphs();
    m_Graph.clear();
    // 清理画笔
    for (auto &tag : m_Tag) {
        delete tag;
    }
    m_Tag.clear();

    // 根据传感器数量添加新的图形
    m_sensorsCount = sensorsCount;
    addGraphs();
}

/// @brief 设置X轴为数字类型
void MyCustomPlot::setXAxisToNumber()
{
    m_xType = MyCustomPlot::KNumber;
    // 清理所有数据
    for (int j = 0; j < m_sensorsCount; j++) {
        m_Graph[j]->data()->clear();
    }
    // 创建一个新的 QCPAxisTicker 刻度对象，适用于数字轴
    QSharedPointer<QCPAxisTicker> numericTicker(new QCPAxisTicker);
    m_Plot->xAxis->setTicker(numericTicker);
    m_Plot->xAxis->setRange(m_currentkey, m_rangeSize, Qt::AlignRight);
    m_Plot->replot(); // 立刻刷新
}

/// @brief 设置X轴为时间类型
void MyCustomPlot::setXAxisToTime(double freq)
{
    if (freq >= 1000) {
        m_rangeTime = 2;
    }
    m_xType = MyCustomPlot::KdateTime;
    // 清理所有数据
    for (int j = 0; j < m_sensorsCount; j++) {
        m_Graph[j]->data()->clear();
    }
    creatTimeTicker();
    m_currentkey = 0;
    m_Plot->replot(); // 立刻刷新
    setStartTime();
}

/// @brief 创建时间类型的X轴
void MyCustomPlot::creatTimeTicker()
{
    // 创建一个新的 QCPAxisTickerDateTime 刻度对象，适用于时间轴
    QSharedPointer<QCPAxisTickerDateTime> dateTimeTicker(new QCPAxisTickerDateTime);
    m_Plot->xAxis->setTicker(dateTimeTicker);
    dateTimeTicker->setDateTimeFormat("yyyy-MM-dd HH:mm:ss.zzz"); // 精确到毫秒

    double nowtime = QDateTime::currentMSecsSinceEpoch();                  // 获取当前时间总的毫秒数
    m_Plot->xAxis->setRange(nowtime * 0.001, m_rangeTime, Qt::AlignRight); // 坐标范围为20秒以内的数据
}

/// @brief 创建QCPGraph对象，切换传感器数量时需要重新创建
void MyCustomPlot::addGraphs()
{
    m_Plot->axisRect()->axis(QCPAxis::atRight, 0)->setPadding(30); // 添加一些填充为“画笔”留出空间
    for (int i = 0; i < m_sensorsCount; i++) {
        // 创建画布(图表)
        QPointer<QCPGraph> graph = m_Plot->addGraph(m_Plot->xAxis, m_Plot->yAxis);
        m_Graph.push_back(graph);
        graph->setPen(QPen(m_graphPens[i]));
        // 创建画笔
        QPointer<AxisTag> tag = new AxisTag(graph->valueAxis());
        m_Tag.push_back(tag);
        tag->setPen(graph->pen());
    }
    if (m_xType == MyCustomPlot::KdateTime) {
        creatTimeTicker();
    } else {
        m_Plot->xAxis->setRange(m_currentkey, m_rangeSize, Qt::AlignRight);
    }
    m_Plot->replot(QCustomPlot::rpQueuedReplot);
}

/// @brief 显示指定的曲线
/// @param sensorsCount
void MyCustomPlot::displaysTheCurve(int sensorsIndex)
{
    // sensorsIndex 为0时全显示
    for (int j = 0; j < m_sensorsCount; j++) {
        if (sensorsIndex != 0 && j != sensorsIndex - 1) {
            m_Graph[j]->setVisible(false);
        } else {
            m_Graph[j]->setVisible(true);
        }
    }
}